#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author: zl
import signal
import platform
import time
import threading
import traceback
from datetime import datetime, timedelta
from queue import Queue
from common.logger import Logger
from enum import Enum
from multiprocessing import cpu_count
import pymysql
import random
logger = Logger("schedule.log").getLogger()


MAX_THREAD_NUM = cpu_count() * 2
G_RUN_FLAG = True
SLEEP_TIME = 1
QUEUE_SIZE = 1000
BATCH_CNT = 50

database_conf = {
                  'host': "10.129.17.207",
                  'port': 3306,
                  'user': "sogou",
                  'passwd':"sogou",
                  'db':"sogou",
                  'charset':"utf8"
                 }

class Status():
    # status: waiting、appending、running、success、fail
    Waiting = 'Waiting'
    Appending = 'Appending'
    Running = 'Running'
    Success = 'Success'
    Fail = 'Fail'


class ProducerExecute:
    def __init__(self):
        self.conn = pymysql.connect(**database_conf)
        self.conn.autocommit(1)

    def get_available_tasks(self):
        tasks = []
        try:
            cursor = self.conn.cursor()
            select_sql = """select id, status, utime from task_tbl 
                            where status='%(waiting)s' or status='%(fail)s' limit %(batch_cnt)d""" % {
                                                                                'waiting': Status.Waiting,
                                                                                'fail': Status.Fail,
                                                                                'batch_cnt': BATCH_CNT
                                                                                }

            cursor.execute(select_sql)
            data = cursor.fetchall()
            if data is not None:
                tasks = data
        except:
            logger.error(traceback.format_exc())

        return tasks


class ConsumerExecute:
    def __init__(self):
        self.conn = pymysql.connect(**database_conf)
        self.conn.autocommit(1)
    def run_task(self, task):
        try:
            cursor = self.conn.cursor()
            now_time = datetime.now()
            timestamp = now_time.strftime('%Y-%m-%d %H:%M:%S')
            res = self.func(task)
            status = res[1]
            update_sql = """update task_tbl set status='%(status)s', utime='%(timestamp)s' 
                            where id=%(id)d""" % {
                                                   'status': status,
                                                   'timestamp': timestamp,
                                                   'id':task[0]
                                                  }
            cursor.execute(update_sql)
        except:
            logger.error(traceback.format_exc())

    def func(self, task):
        time.sleep(1)
        status_code = {
            1: 'Success',
            2: 'Fail',
            3: 'Success',
            4: 'Success',
            5: 'Fail'
        }
        i = random.randrange(1,6,1)
        return (task[0], status_code[i], task[2])


def signal_runflag(sig, frame):
    global G_RUN_FLAG
    if sig == signal.SIGHUP:
        logger.info("receive HUP signal ")
        G_RUN_FLAG = False


class Producer(threading.Thread):
    def __init__(self, queue, sleep_time):
        threading.Thread.__init__(self)
        self.queue = queue
        self.sleep_time = sleep_time
        self.execute = ProducerExecute()

    def run(self):
        print('produce start')
        # if True:
        while G_RUN_FLAG:
            try:
                while self.queue.qsize() + BATCH_CNT >= QUEUE_SIZE:
                    time.sleep(self.sleep_time)

                #每批最多BATCH_CNT个task
                for task in self.execute.get_available_tasks():
                    cursor = self.execute.conn.cursor()
                    now_time = datetime.now()
                    timestamp = now_time.strftime('%Y-%m-%d %H:%M:%S')
                    update_sql = """update task_tbl set status='%(status)s', utime='%(timestamp)s' 
                                    where id=%(id)d""" % {
                                                          'status': Status.Appending,
                                                          'timestamp': timestamp,
                                                          'id': task[0]
                                                         }
                    cursor.execute(update_sql)
                    self.queue.put(task)
                print('queueed tasks: ' + str(self.queue.qsize()))
            except:
                logger.error(traceback.format_exc())
                print(traceback.format_exc())



class Consumer(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.execute = ConsumerExecute()

    def run(self):
        print('consumer start')
        # if True:
        while G_RUN_FLAG:
            try:
                task = self.queue.get()
                time.sleep(1)
                cursor = self.execute.conn.cursor()
                now_time = datetime.now()
                timestamp = now_time.strftime('%Y-%m-%d %H:%M:%S')
                update_sql = """update task_tbl set status='%(status)s', utime='%(timestamp)s' 
                                where id=%(id)d""" % {
                                                      'status': Status.Running,
                                                      'timestamp': timestamp,
                                                      'id': task[0]
                                                      }
                cursor.execute(update_sql)
                self.execute.run_task(task)
            except:
                logger.error(traceback.format_exc())
                print(traceback.format_exc())

def main():
    queue = Queue(QUEUE_SIZE)
    producer = Producer(queue, SLEEP_TIME)
    producer.start()

    for i in range(MAX_THREAD_NUM):
        consumer = Consumer(queue)
        consumer.setDaemon(True)
        consumer.start()

    producer.join()


if __name__ == "__main__":
    logger.info("execute run")
    if platform.system() == "Linux":
        signal.signal(signal.SIGHUP, signal_runflag)
    main()
    logger.info("execute exit.")